home *** CD-ROM | disk | FTP | other *** search
- #define LIBQBUILD_CORE
- #include "../include/libqbuild.h"
-
- int leaffaces; /* 4 */
- int nodefaces; /* 4 */
- int splitnodes; /* 4 */
-
- int c_solid, c_empty, c_water; /* 12 */
-
- bool usemidsplit; /* ? */
-
- /*============================================================================ */
-
- /*
- * ==================
- * FaceSide
- *
- * For BSP hueristic
- * ==================
- */
- int FaceSide(register struct visfacet *in, register struct plane *split)
- {
- int frontcount, backcount;
- vec_t dot;
- int i;
- vec_t *p;
-
- frontcount = backcount = 0;
-
- if (split->type < 3)
- /* axial planes are fast */
- for (i = 0, p = in->pts[0] + split->type; i < in->numpoints; i++, p += 3) {
- if (*p > split->dist + ON_EPSILON) {
- if (backcount)
- return SIDE_ON;
- frontcount = 1;
- }
- else if (*p < split->dist - ON_EPSILON) {
- if (frontcount)
- return SIDE_ON;
- backcount = 1;
- }
- }
- else
- /* sloping planes take longer */
- for (i = 0, p = in->pts[0]; i < in->numpoints; i++, p += 3) {
- dot = DotProduct(p, split->normal);
- dot -= split->dist;
- if (dot > ON_EPSILON) {
- if (backcount)
- return SIDE_ON;
- frontcount = 1;
- }
- else if (dot < -ON_EPSILON) {
- if (frontcount)
- return SIDE_ON;
- backcount = 1;
- }
- }
-
- if (!frontcount)
- return SIDE_BACK;
- if (!backcount)
- return SIDE_FRONT;
-
- return SIDE_ON;
- }
-
- /*
- * ==================
- * ChooseMidPlaneFromList
- *
- * The clipping hull BSP doesn't worry about avoiding splits
- * ==================
- */
- struct surface *ChooseMidPlaneFromList(__memBase, register struct surface *surfaces, register vec3_t mins, register vec3_t maxs)
- {
- int j, l;
- struct surface *p, *bestsurface;
- vec_t bestvalue, value, dist;
- struct plane *plane;
-
- /* pick the plane that splits the least */
- bestvalue = 6 * 8192 * 8192;
- bestsurface = NULL;
-
- for (p = surfaces; p; p = p->next) {
- if (p->onnode)
- continue;
-
- #ifdef EXHAUSIVE_CHECK
- if (p->planenum >= bspMem->numbrushplanes || p->planenum < 0)
- Error("looking for nonexisting plane %d\n", p->planenum);
- #endif
- plane = &bspMem->brushplanes[p->planenum];
-
- /* check for axis aligned surfaces */
- l = plane->type;
- if (l > PLANE_Z)
- continue;
-
- /* calculate the split metric along axis l, smaller values are better */
- value = 0;
-
- dist = plane->dist * plane->normal[l];
- for (j = 0; j < 3; j++) {
- if (j == l) {
- value += (maxs[l] - dist) * (maxs[l] - dist);
- value += (dist - mins[l]) * (dist - mins[l]);
- }
- else
- value += 2 * (maxs[j] - mins[j]) * (maxs[j] - mins[j]);
- }
-
- if (value > bestvalue)
- continue;
-
- /* currently the best! */
- bestvalue = value;
- bestsurface = p;
- }
-
- if (!bestsurface) {
- for (p = surfaces; p; p = p->next)
- if (!p->onnode)
- return p; /* first valid surface */
-
- Error("ChooseMidPlaneFromList: no valid planes");
- }
-
- return bestsurface;
- }
-
- /*
- * ==================
- * ChoosePlaneFromList
- *
- * The real BSP hueristic
- * ==================
- */
- struct surface *ChoosePlaneFromList(__memBase, register struct surface *surfaces, register vec3_t mins, register vec3_t maxs, register bool usefloors)
- {
- int j, k, l;
- struct surface *p, *p2, *bestsurface;
- vec_t bestvalue, bestdistribution, value, dist;
- struct plane *plane;
- struct visfacet *f;
-
- /* pick the plane that splits the least */
- bestvalue = 99999;
- bestsurface = NULL;
- bestdistribution = 9e30;
-
- for (p = surfaces; p; p = p->next) {
- if (p->onnode)
- continue;
-
- #ifdef EXHAUSIVE_CHECK
- if (p->planenum >= bspMem->numbrushplanes || p->planenum < 0)
- Error("looking for nonexisting plane %d\n", p->planenum);
- #endif
- plane = &bspMem->brushplanes[p->planenum];
- k = 0;
-
- if (!usefloors && plane->normal[2] == 1)
- continue;
-
- for (p2 = surfaces; p2; p2 = p2->next) {
- if (p2 == p)
- continue;
- if (p2->onnode)
- continue;
-
- for (f = p2->faces; f; f = f->next) {
- if (FaceSide(f, plane) == SIDE_ON) {
- k++;
- if (k >= bestvalue)
- break;
- }
-
- }
- if (k > bestvalue)
- break;
- }
-
- if (k > bestvalue)
- continue;
-
- /* if equal numbers, axial planes win, then decide on spatial subdivision */
- if (k < bestvalue || (k == bestvalue && plane->type < PLANE_ANYX)) {
- /* check for axis aligned surfaces */
- l = plane->type;
-
- if (l <= PLANE_Z) { /* axial aligned */
- /* calculate the split metric along axis l */
- value = 0;
-
- for (j = 0; j < 3; j++) {
- if (j == l) {
- dist = plane->dist * plane->normal[l];
- value += (maxs[l] - dist) * (maxs[l] - dist);
- value += (dist - mins[l]) * (dist - mins[l]);
- }
- else
- value += 2 * (maxs[j] - mins[j]) * (maxs[j] - mins[j]);
- }
-
- if (value > bestdistribution && k == bestvalue)
- continue;
- bestdistribution = value;
- }
- /* currently the best! */
- bestvalue = k;
- bestsurface = p;
- }
-
- }
-
- return bestsurface;
- }
-
- /*
- * ==================
- * SelectPartition
- *
- * Selects a surface from a linked list of surfaces to split the group on
- * returns NULL if the surface list can not be divided any more (a leaf)
- * ==================
- */
- struct surface *SelectPartition(__memBase, register struct surface *surfaces)
- {
- int i;
- short int j;
- vec3_t mins, maxs;
- struct surface *p, *bestsurface;
-
- /* count onnode surfaces */
- i = 0;
- bestsurface = NULL;
- for (p = surfaces; p; p = p->next)
- if (!p->onnode) {
- i++;
- bestsurface = p;
- }
-
- if (i == 0)
- return NULL;
-
- if (i == 1)
- return bestsurface; /* this is a final split */
-
- /* calculate a bounding box of the entire surfaceset */
- for (i = 0; i < 3; i++) {
- mins[i] = 99999;
- maxs[i] = -99999;
- }
-
- for (p = surfaces; p; p = p->next)
- for (j = 0; j < 3; j++) {
- if (p->mins[j] < mins[j])
- mins[j] = p->mins[j];
- if (p->maxs[j] > maxs[j])
- maxs[j] = p->maxs[j];
- }
-
- if (usemidsplit) /* do fast way for clipping hull */
- return ChooseMidPlaneFromList(bspMem, surfaces, mins, maxs);
-
- /* do slow way to save poly splits for drawing hull */
- #if 0
- bestsurface = ChoosePlaneFromList(surfaces, mins, maxs, FALSE);
- if (bestsurface)
- return bestsurface;
- #endif
- return ChoosePlaneFromList(bspMem, surfaces, mins, maxs, TRUE);
- }
-
- /*============================================================================ */
-
- /*
- * =================
- * CalcSurfaceInfo
- *
- * Calculates the bounding box
- * =================
- */
- void CalcSurfaceInfo(register struct surface *surf)
- {
- int i;
- short int j;
- struct visfacet *f;
-
- if (!surf->faces)
- Error("CalcSurfaceInfo: surface without a face");
-
- /* calculate a bounding box */
- for (i = 0; i < 3; i++) {
- surf->mins[i] = 99999;
- surf->maxs[i] = -99999;
- }
-
- for (f = surf->faces; f; f = f->next) {
- if (f->contents[0] >= 0 || f->contents[1] >= 0)
- Error("Bad contents");
- for (i = 0; i < f->numpoints; i++)
- for (j = 0; j < 3; j++) {
- if (f->pts[i][j] < surf->mins[j])
- surf->mins[j] = f->pts[i][j];
- if (f->pts[i][j] > surf->maxs[j])
- surf->maxs[j] = f->pts[i][j];
- }
- }
- }
-
- /*
- * ==================
- * DividePlane
- * ==================
- */
- void DividePlane(__memBase, register struct surface *in, register struct plane *split, register struct surface **front, register struct surface **back)
- {
- struct visfacet *facet, *next;
- struct visfacet *frontlist, *backlist;
- struct visfacet *frontfrag, *backfrag;
- struct surface *news;
- struct plane *inplane;
-
- #ifdef EXHAUSIVE_CHECK
- if (inplane->planenum >= bspMem->numbrushplanes || inplane->planenum < 0)
- Error("looking for nonexisting plane %d\n", inplane->planenum);
- #endif
- inplane = &bspMem->brushplanes[in->planenum];
-
- /* parallel case is easy */
- if (VectorCompare(inplane->normal, split->normal)) {
- /* check for exactly on node */
- if (inplane->dist == split->dist) { /* divide the facets to the front and back sides */
-
- news = AllocSurface();
- *news = *in;
-
- facet = in->faces;
- in->faces = NULL;
- news->faces = NULL;
- in->onnode = news->onnode = TRUE;
-
- for (; facet; facet = next) {
- next = facet->next;
- if (facet->planeside == 1) {
- facet->next = news->faces;
- news->faces = facet;
- }
- else {
- facet->next = in->faces;
- in->faces = facet;
- }
- }
-
- if (in->faces)
- *front = in;
- else
- *front = NULL;
- if (news->faces)
- *back = news;
- else
- *back = NULL;
- return;
- }
-
- if (inplane->dist > split->dist) {
- *front = in;
- *back = NULL;
- }
- else {
- *front = NULL;
- *back = in;
- }
- return;
- }
-
- /*
- * do a real split. may still end up entirely on one side
- * OPTIMIZE: use bounding box for fast test
- */
- frontlist = NULL;
- backlist = NULL;
-
- for (facet = in->faces; facet; facet = next) {
- next = facet->next;
- SplitFace(facet, split, &frontfrag, &backfrag);
- if (frontfrag) {
- frontfrag->next = frontlist;
- frontlist = frontfrag;
- }
- if (backfrag) {
- backfrag->next = backlist;
- backlist = backfrag;
- }
- }
-
- /* if nothing actually got split, just move the in plane */
- if (frontlist == NULL) {
- *front = NULL;
- *back = in;
- in->faces = backlist;
- return;
- }
-
- if (backlist == NULL) {
- *front = in;
- *back = NULL;
- in->faces = frontlist;
- return;
- }
-
- /* stuff got split, so allocate one new plane and reuse in */
- news = AllocSurface();
- *news = *in;
- news->faces = backlist;
- *back = news;
-
- in->faces = frontlist;
- *front = in;
-
- /* recalc bboxes and flags */
- CalcSurfaceInfo(news);
- CalcSurfaceInfo(in);
- }
-
- /*
- * ==================
- * DivideNodeBounds
- * ==================
- */
- void DivideNodeBounds(register struct node *node, register struct plane *split)
- {
- VectorCopy(node->mins, node->children[0]->mins);
- VectorCopy(node->mins, node->children[1]->mins);
- VectorCopy(node->maxs, node->children[0]->maxs);
- VectorCopy(node->maxs, node->children[1]->maxs);
-
- /* OPTIMIZE: sloping cuts can give a better bbox than this... */
- if (split->type > 2)
- return;
-
- node->children[0]->mins[split->type] =
- node->children[1]->maxs[split->type] = split->dist;
- }
-
- /*
- * ==================
- * LinkConvexFaces
- *
- * Determines the contents of the leaf and creates the final list of
- * original faces that have some fragment inside this leaf
- * ==================
- */
- void LinkConvexFaces(register struct surface *planelist, register struct node *leafnode)
- {
- struct visfacet *f, *next;
- struct surface *surf, *pnext;
- int i, count;
-
- leafnode->faces = NULL;
- leafnode->contents = 0;
- leafnode->planenum = -1;
-
- count = 0;
- for (surf = planelist; surf; surf = surf->next) {
- for (f = surf->faces; f; f = f->next) {
- count++;
- if (!leafnode->contents)
- leafnode->contents = f->contents[0];
- else if (leafnode->contents != f->contents[0])
- Error("Mixed face contents in leafnode");
- }
- }
-
- if (!leafnode->contents)
- leafnode->contents = CONTENTS_SOLID;
-
- switch (leafnode->contents) {
- case CONTENTS_EMPTY:
- c_empty++;
- break;
- case CONTENTS_SOLID:
- c_solid++;
- break;
- case CONTENTS_WATER:
- case CONTENTS_SLIME:
- case CONTENTS_LAVA:
- case CONTENTS_SKY:
- c_water++;
- break;
- default:
- Error("LinkConvexFaces: bad contents number");
- }
-
- /* write the list of faces, and free the originals */
- leaffaces += count;
- if (!(leafnode->markfaces = (struct visfacet **)tmalloc(sizeof(struct visfacet *) * (count + 1))))
- Error(failed_memoryunsize, "markfaces");
-
- i = 0;
- for (surf = planelist; surf; surf = pnext) {
- pnext = surf->next;
- for (f = surf->faces; f; f = next) {
- next = f->next;
- leafnode->markfaces[i] = f->original;
- i++;
- FreeFace(f);
- }
- FreeSurface(surf);
- }
- leafnode->markfaces[i] = NULL; /* sentinal */
-
- }
-
- /*
- * ==================
- * LinkNodeFaces
- *
- * Returns a duplicated list of all faces on surface
- * ==================
- */
- struct visfacet *LinkNodeFaces(__memBase, register struct surface *surface)
- {
- struct visfacet *f, *new, **prevptr;
- struct visfacet *list;
-
- list = NULL;
-
- /* subdivide */
- prevptr = &surface->faces;
- while (1) {
- f = *prevptr;
- if (!f)
- break;
- SubdivideFace(bspMem, f, prevptr);
- f = *prevptr;
- prevptr = &f->next;
- }
-
- /* copy */
- for (f = surface->faces; f; f = f->next) {
- nodefaces++;
- new = AllocFace(f->numpoints);
- CopyFace(new, f);
- f->original = new;
- new->next = list;
- list = new;
- }
-
- return list;
- }
-
- /*
- * ==================
- * PartitionSurfaces
- * ==================
- */
- void PartitionSurfaces(__memBase, register struct surface *surfaces, register struct node *node)
- {
- struct surface *split, *p, *next;
- struct surface *frontlist, *backlist;
- struct surface *frontfrag, *backfrag;
- struct plane *splitplane;
-
- split = SelectPartition(bspMem, surfaces);
- if (!split) { /* this is a leaf node */
-
- node->planenum = PLANENUM_LEAF;
- LinkConvexFaces(surfaces, node);
- return;
- }
-
- splitnodes++;
- node->faces = LinkNodeFaces(bspMem, split);
- node->children[0] = AllocNode();
- node->children[1] = AllocNode();
- node->planenum = split->planenum;
-
- #ifdef EXHAUSIVE_CHECK
- if (split->planenum >= bspMem->numbrushplanes || split->planenum < 0)
- Error("looking for nonexisting plane %d\n", split->planenum);
- #endif
- splitplane = &bspMem->brushplanes[split->planenum];
-
- DivideNodeBounds(node, splitplane);
-
- /* multiple surfaces, so split all the polysurfaces into front and back lists */
- frontlist = NULL;
- backlist = NULL;
-
- for (p = surfaces; p; p = next) {
- next = p->next;
- DividePlane(bspMem, p, splitplane, &frontfrag, &backfrag);
- if (frontfrag && backfrag) {
- /*
- * the plane was split, which may expose oportunities to merge
- * adjacent faces into a single face
- * MergePlaneFaces (frontfrag);
- * MergePlaneFaces (backfrag);
- */
- }
-
- if (frontfrag) {
- if (!frontfrag->faces)
- Error("surface with no faces");
- frontfrag->next = frontlist;
- frontlist = frontfrag;
- }
- if (backfrag) {
- if (!backfrag->faces)
- Error("surface with no faces");
- backfrag->next = backlist;
- backlist = backfrag;
- }
- }
-
- PartitionSurfaces(bspMem, frontlist, node->children[0]);
- PartitionSurfaces(bspMem, backlist, node->children[1]);
- }
-
- /*
- * ==================
- * DrawSurface
- * ==================
- */
- void DrawSurface(register struct surface *surf)
- {
- struct visfacet *f;
-
- for (f = surf->faces; f; f = f->next)
- Draw_DrawFace(f);
- }
-
- /*
- * ==================
- * DrawSurfaceList
- * ==================
- */
- void DrawSurfaceList(register struct surface *surf)
- {
- Draw_ClearWindow();
- while (surf) {
- DrawSurface(surf);
- surf = surf->next;
- }
- }
-
- /*
- * ==================
- * SolidBSP
- * ==================
- */
- struct node *SolidBSP(__memBase, struct surface *surfhead, bool midsplit)
- {
- short int i;
- struct node *headnode;
-
- mprintf("----- SolidBSP ----------\n");
-
- headnode = AllocNode();
- usemidsplit = midsplit;
-
- /* calculate a bounding box for the entire model */
- for (i = 0; i < 3; i++) {
- headnode->mins[i] = brushset->mins[i] - SIDESPACE;
- headnode->maxs[i] = brushset->maxs[i] + SIDESPACE;
- }
-
- /* recursively partition everything */
- Draw_ClearWindow();
- splitnodes = 0;
- leaffaces = 0;
- nodefaces = 0;
- c_solid = c_empty = c_water = 0;
-
- PartitionSurfaces(bspMem, surfhead, headnode);
-
- mprintf("%5i split nodes\n", splitnodes);
- mprintf("%5i solid leafs\n", c_solid);
- mprintf("%5i empty leafs\n", c_empty);
- mprintf("%5i water leafs\n", c_water);
- mprintf("%5i leaffaces\n", leaffaces);
- mprintf("%5i nodefaces\n", nodefaces);
-
- return headnode;
- }
-